package com.atguigu.springcloud.common;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import javax.annotation.PostConstruct;

/**
 * @author xiaolong
 * @version 1.0
 * @description: spel语法获取相应的值
 * @date 2021/10/18 16:47
 */
public abstract class AbstractSpelHandler {


    private SpelParserConfiguration configuration;
    private ExpressionParser parser;

    private BeanFactoryResolver resolver;

    private ParserContext parserContext;

    @Autowired
    private ApplicationContext applicationContext;

    public AbstractSpelHandler() {
    }

    @PostConstruct
    private void init() {
        //设置编译器的模式
        this.configuration = new SpelParserConfiguration(SpelCompilerMode.MIXED, this.getClass().getClassLoader());
        this.parser = new SpelExpressionParser(this.configuration);
        this.resolver = new BeanFactoryResolver(this.applicationContext);
        this.parserContext = new ParserContext() {
            public boolean isTemplate() {
                return true;
            }

            public String getExpressionPrefix() {
                return "{";
            }

            public String getExpressionSuffix() {
                return "}";
            }
        };
    }


    /**
     * @param joinPoint SpringAOP通过JoinPoint获取参数名和值
     * @return
     * @description: TODO
     */
    protected String parseSpel(ProceedingJoinPoint joinPoint, String spel) {
        // 下面两个数组中，参数值和参数名的个数和位置是一一对应的。
        //获取参数值
        Object[] args = joinPoint.getArgs();
        //获取被增强的方法相关 信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //参数名
        String[] parameterNames = signature.getParameterNames();
        //创建一个虚拟的容器EvaluationContext
        StandardEvaluationContext context = new StandardEvaluationContext();
        //在SPEL中使用变量的示例 在SpEL中, 我们可以将值存储在变量中,
        // 然后在方法中使用变量并调用方法
        for (int len = 0; len < parameterNames.length; ++len) {
            //参数名：参数值
            context.setVariable(parameterNames[len], args[len]);  //setVariable（“clientid”，“1325452”）
        }
        //setRootObject并非必须；一个EvaluationContext只能有一个RootObject，引用它的属性时，可以不加前缀
        context.setRootObject(this.applicationContext);
        context.setBeanResolver(this.resolver);
        //将字符串表达式转为Expression对象
        Expression expression = this.parser.parseExpression(spel, this.parserContext);   //如果这个为 #{clientid}
        return (String) expression.getValue(context, String.class);//返回值就为1325452

    }


}
